using Crux.Core.Dapper.SqlGenerator;
using Crux.Core.Dapper.SqlGenerator.Filters;
using Crux.Core.Data;

namespace Crux.Core.Dapper;
public partial class QueryRepository<TContext>
    where TContext : DapperContext
{
    /// <inheritdoc/>
    public IEnumerable<TEntity> SqlQueryRaw<TEntity>(
        string sql,
        object? parameters = null)
    {
        if (string.IsNullOrEmpty(sql))
        {
            throw new ArgumentNullException(nameof(sql));
        }

        return Query<TEntity>(sql, parameters);
    }
    /// <inheritdoc/>
    public async Task<IEnumerable<TEntity>> SqlQueryRawAsync<TEntity>(
        string sql,
        object? parameters = null,
        CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrEmpty(sql))
        {
            throw new ArgumentNullException(nameof(sql));
        }

        return await QueryAsync<TEntity>(sql, parameters, cancellationToken).ConfigureAwait(false);
    }

    /// <inheritdoc/>
    public IEnumerable<TEntity> Query<TEntity>(
        Expression<Func<TEntity, bool>> predicate,
        Expression<Func<TEntity, object>>? expr = null,
        OrderExpression<TEntity>? orderExpression = null,
        uint limit = 0,
        uint offset = 0,
        bool ignoreFilter = false,
        Action<SqlGenerator<TEntity>>? configure = null,
        params Expression<Func<TEntity, object>>[] includes) where TEntity : class
    {
        var sqlQuery = GetSqlQuery(predicate, expr, orderExpression, limit, offset, ignoreFilter, configure, includes);

        return Query<TEntity>(sqlQuery.GetSql(), sqlQuery.Param);
    }

    private SqlQuery GetSqlQuery<TEntity>(
        Expression<Func<TEntity, bool>> predicate,
        Expression<Func<TEntity, object>>? expr = null,
        OrderExpression<TEntity>? orderExpression = null,
        uint limit = 0,
        uint offset = 0,
        bool ignoreFilter = false,
        Action<SqlGenerator<TEntity>>? configure = null,
        params Expression<Func<TEntity, object>>[] includes) where TEntity : class
    {
        var generator = this.GetSqlGenerator<TEntity>(configure);

        FilterData? filterData = null;
        if (expr is not null)
        {
            filterData = new FilterData().BySelect(expr);
        }
        if (orderExpression is not null)
        {
            filterData ??= new FilterData();
            filterData.ByOrder(orderExpression);
        }
        if (limit > 0 || offset > 0)
        {
            filterData ??= new FilterData();
            filterData.ByLimit(limit, offset);
        }

        var sqlQuery = generator.GetSelectAll(predicate, filterData, ignoreFilter, includes);
        return sqlQuery;
    }

    /// <inheritdoc/>
    public async Task<IEnumerable<TEntity>> QueryAsync<TEntity>(
        Expression<Func<TEntity, bool>> predicate,
        Expression<Func<TEntity, object>>? expr = null,
        OrderExpression<TEntity>? orderExpression = null,
        uint limit = 0,
        uint offset = 0,
        bool ignoreFilter = false,
        Action<SqlGenerator<TEntity>>? configure = null,
        CancellationToken cancellationToken = default,
        params Expression<Func<TEntity, object>>[] includes) where TEntity : class
    {
        var sqlQuery = GetSqlQuery(predicate, expr, orderExpression, limit, offset, ignoreFilter, configure, includes);

        return await QueryAsync<TEntity>(sqlQuery.GetSql(), sqlQuery.Param, cancellationToken).ConfigureAwait(false);

    }
}